import axios from "axios"
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios"
import type { AxiosRequestConfigExtension, CustomCreateAxiosDefaults } from "./axiosTransform"
import { AxiosCanceler } from "./axiosCancel"
import { isFunction } from "@/utils"
import { RequestOptions, ResponseData } from "../types"
import { RequestMethod } from "../enums"

/**
 * @description:  axios模块
 */
export class VAxios {
    private axiosInstance: AxiosInstance
    private options: CustomCreateAxiosDefaults

    constructor(options: CustomCreateAxiosDefaults) {
        this.options = options
        this.axiosInstance = axios.create(options)
        this.setupInterceptors()
    }

    getAxios(): AxiosInstance {
        return this.axiosInstance
    }

    /**
     * @description: 设置通用header
     */
    setHeader(headers: any): void {
        if (!this.axiosInstance) {
            return
        }
        Object.assign(this.axiosInstance.defaults.headers, headers)
    }

    /**
     * @description: 拦截器配置
     */
    private setupInterceptors() {
        const { transform } = this.options
        if (!transform) {
            return
        }
        const { requestInterceptors, requestInterceptorsCatch, responseInterceptors, responseInterceptorsCatch } = transform
        const axiosCanceler = new AxiosCanceler()

        // 请求拦截器配置处理
        this.axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig & AxiosRequestConfigExtension) => {
            const ignoreCancelToken = config.requestOptions?.ignoreCancelToken
            !ignoreCancelToken && axiosCanceler.addPending(config)
            requestInterceptors && isFunction(requestInterceptors) && requestInterceptors(config, config.requestOptions ?? {})
            return config
        }, undefined)

        // 请求拦截器错误捕获
        requestInterceptorsCatch &&
            isFunction(requestInterceptorsCatch) &&
            this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch)

        // 响应结果拦截器处理
        this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
            res && axiosCanceler.removePending(res.config)
            if (responseInterceptors && isFunction(responseInterceptors)) {
                res = responseInterceptors(res)
            }
            return res
        }, undefined)

        // 响应结果拦截器错误捕获
        responseInterceptorsCatch &&
            isFunction(responseInterceptorsCatch) &&
            this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch)
    }

    /**
     * @description:   请求方法
     */
    request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
        const configWrapper: AxiosRequestConfig & AxiosRequestConfigExtension = config
        const { transform, requestOptions } = this.options
        const mergedOptions: RequestOptions = Object.assign({}, requestOptions, options)
        const { beforeRequestHook, requestCatch, transformResponseData } = transform || {}
        beforeRequestHook && isFunction(beforeRequestHook) && beforeRequestHook(configWrapper, mergedOptions)
        configWrapper.requestOptions = mergedOptions

        return new Promise((resolve, reject) => {
            this.axiosInstance
                .request<any, AxiosResponse<ResponseData<T>>>(configWrapper)
                .then(res => {
                    // 请求是否被取消
                    const isCancel = axios.isCancel(res)
                    if (transformResponseData && isFunction(transformResponseData) && !isCancel) {
                        try {
                            const ret = transformResponseData(res, mergedOptions)
                            resolve(ret)
                        } catch (err) {
                            reject(err || new Error("response handle error!"))
                        }
                    } else {
                        resolve(res as unknown as Promise<T>)
                    }
                })
                .catch((e: Error) => {
                    if (requestCatch && isFunction(requestCatch)) {
                        reject(requestCatch(e))
                    } else {
                        reject(e)
                    }
                })
        })
    }

    get<T = any>(url: string, params?: any, options?: RequestOptions): Promise<T> {
        const config = {
            url,
            method: RequestMethod.GET,
            params
        }
        return this.request(config, options)
    }

    post<T = any>(url: string, params?: any, options?: RequestOptions): Promise<T> {
        const config = {
            url,
            method: RequestMethod.POST,
            data: params
        }
        return this.request(config, options)
    }

    put<T = any>(url: string, params?: any, options?: RequestOptions): Promise<T> {
        const config = {
            url,
            method: RequestMethod.PUT,
            data: params
        }
        return this.request(config, options)
    }

    delete<T = any>(url: string, params?: any, options?: RequestOptions): Promise<T> {
        const config = {
            url,
            method: RequestMethod.DELETE,
            data: params
        }
        return this.request(config, options)
    }
}
